传送门:C. Journey
描述:
题意:
DAG图,5000个点,5000条边让你求从1到n的路径长度不超过T中经过点数最多的一条
思路:
f[i][j]表示当前在点i,接下来经过j个点(包括自己)到n点的最小距离
nxt[i][j]表示当前在点i,接下来经过j个点(包括自己)到n点的后继 ,这里用short可以优化不少内存
然后 按照拓扑排序记忆化搜索一下
如果我们有x的后继为y
那么我们肯定可以对y做dfs,在对y做dfs的时候,显然不会走到x(DAG图)
换而言之,y的所有可能的前驱(包括前驱的前驱)都不会影响到dfs(y)的值
于是,这里就保证了该dp的正确拓扑序,也就可以在O(n^2)的复杂度内出解。
代码:
#include <bits/stdc++.h>
#define pr(x) cout << #x << "= " << x << " " ;
#define pl(x) cout << #x << "= " << x << endl;
#define ll __int64
#define mst(ss,b) memset(ss,b,sizeof(ss));
using namespace std;
const int N=5050;
const int inf=0x3f3f3f3f;
int n, m, T;
int f[N][N];//f[i][j]表示当前在点i,接下来经过j个点(包括自己)到n点的最小距离
short nxt[N][N];//nxt[i][j]表示当前在点i,接下来经过j个点(包括自己)到n点的后继
vector< pair<int,int> >a[N];
bool vis[N];
void dfs(int x){
if (vis[x])return; vis[x] = 1;
for (auto it : a[x]){
dfs(it.first);
for (int i = 2; i <= n; ++i){
int dis = f[it.first][i - 1] + it.second;
if (dis < f[x][i]){
f[x][i] = dis;
nxt[x][i] = it.first;
}
}
}
}
void print(){
for (int i = n; ; --i)if (f[1][i] <= T){
printf("%d\n", i);
int x = 1; printf("%d ", x);
while (x != n){
x = nxt[x][i--];
printf("%d ", x);
}puts("");
break;
}
}
int main(){
while(~scanf("%d%d%d",&n, &m, &T)){
for (int i = 1; i <= n; ++i)a[i].clear(), vis[i] = 0;
for (int i = 1; i <= m; ++i){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
if (y == 1 || x == n)continue;
a[x].push_back({ y,z });
}
mst(f, inf);
f[n][1] = 0; vis[n] = 1;
dfs(1);
print();
}
return 0;
}